package com.next.aap.server.services;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import com.next.aap.client.AssemblyConstituencyDto;
import com.next.aap.client.AssemblyConstituencyWeb;
import com.next.aap.client.CandidateDto;
import com.next.aap.client.DistrictDto;
import com.next.aap.client.DistrictWeb;
import com.next.aap.client.ManifestoDto;
import com.next.aap.client.RegisterDeviceResponse;
import com.next.aap.client.RegisterFacebookUserProfile;
import com.next.aap.client.RegisterFacebookUserResponse;
import com.next.aap.client.StateDto;
import com.next.aap.client.UserDeviceWeb;
import com.next.aap.messages.FacebookUserRegisteredMessage;
import com.next.aap.messages.TaskStatus;
import com.next.aap.server.controller.MessageSender;
import com.next.aap.server.persistance.AssemblyConstituency;
import com.next.aap.server.persistance.Candidate;
import com.next.aap.server.persistance.District;
import com.next.aap.server.persistance.FacebookAccount;
import com.next.aap.server.persistance.Manifesto;
import com.next.aap.server.persistance.State;
import com.next.aap.server.persistance.User;
import com.next.aap.server.persistance.UserDevice;
import com.next.aap.server.persistance.helper.AssemblyConstituencyHelper;
import com.next.aap.server.persistance.helper.CandidateHelper;
import com.next.aap.server.persistance.helper.DistrictHelper;
import com.next.aap.server.persistance.helper.FacebookAccountHelper;
import com.next.aap.server.persistance.helper.ManifestoHelper;
import com.next.aap.server.persistance.helper.StateHelper;
import com.next.aap.server.persistance.helper.UserDeviceHelper;
import com.next.aap.server.persistance.helper.UserHelper;
import com.next.aap.server.tasks.RegisterDeviceTask;
import com.next.aap.server.tasks.RegisterFacebookUserTask;
import com.next.core.exception.AppException;

@Component
public class AppServiceImpl implements ApplicationContextAware, AppService{

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	private ApplicationContext applicationContext;
	
	@Autowired
	@Qualifier(value="httpRequestExecutor")
	private ThreadPoolTaskExecutor threadPoolTaskExecutor;
	
	@Autowired
	private UserHelper userHelper;
	@Autowired
	UserDeviceHelper userDeviceHelper;
	
	@Autowired
	FacebookAccountHelper facebookAccountHelper;
	
	@Autowired
	private StateHelper stateHelper;
	
	@Autowired
	private DistrictHelper districtHelper;
	
	@Autowired
	private AssemblyConstituencyHelper assemblyConstituencyHelper;
	
	@Autowired
	private CandidateHelper candidateHelper;
	
	@Autowired
	private ManifestoHelper manifestoHelper;
	
	@Autowired
	private MessageSender messageSender;
	
	private Set<String> deviceList = new HashSet<String>();

	/* (non-Javadoc)
	 * @see com.next.aap.server.services.AppService#registerDevice(java.lang.String)
	 */
	public RegisterDeviceResponse registerDevice(String deviceRegId)
			throws AppException {
		deviceList.add(deviceRegId);
		RegisterDeviceResponse registerDeviceResponse = new RegisterDeviceResponse();
		UserDevice userDevice = userDeviceHelper.getUserDeviceByRegId(deviceRegId);
		if(userDevice == null){
			userDevice = new UserDevice();
			userDevice.setDateCreated(new Date());
			userDevice.setDeviceType("Android");
			userDevice.setRegId(deviceRegId);
			userDevice.setDateModified(new Date());
			userDevice = userDeviceHelper.saveUserDevice(userDevice);
			registerDeviceResponse.setStatus(TaskStatus.STATUS_COMPLETED);
			logger.info("User Device Saved Succesfully");
			
		}else{
			registerDeviceResponse.setStatus(TaskStatus.STATUS_ALREADY_REGISTERED);
			logger.info("User Device already registered");
		}
		return registerDeviceResponse;
		
	}
	
	public RegisterDeviceResponse registerDeviceAsync(String deviceRegId) throws AppException {
		RegisterDeviceResponse registerDeviceResponse = new RegisterDeviceResponse();
		try{
			RegisterDeviceTask registerDeviceTask = applicationContext.getBean(RegisterDeviceTask.class);
			registerDeviceTask.setDeviceRegId(deviceRegId);
			threadPoolTaskExecutor.execute(registerDeviceTask);
			registerDeviceResponse.setStatus(TaskStatus.STATUS_RUNNING_IN_BACKGROUND);
		}catch(Exception ex){
			registerDeviceResponse.setStatus(TaskStatus.STATUS_FAILED);
		}
		return registerDeviceResponse;
	}

	public String showDevices() {
		StringBuilder sb = new StringBuilder("");

		for (String oneDevice : deviceList) {
			sb.append(oneDevice);
			sb.append(" , ");
		}
		return sb.toString();
	}
	
    public String sendMessage(String msg){
    	System.out.println("Sending message : "+ msg);
    	
		try {
			messageSender.sendMessage(msg, new ArrayList<String>(deviceList));
			
		} catch (IOException e) {
			e.printStackTrace();
			return "Error : "+e.getMessage();
		}
		
		return "Message Sent";
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
		
	}

	
	/**
	 * Current approach is if a request with new facebook account comes we will create a new user
	 * and currently do not support to add multiple facebook account into one User
	 */
	public FacebookUserRegisteredMessage registerFacebookUser(
			RegisterFacebookUserProfile registerFacebookUserProfile)
			throws AppException {
		//First register User Device
		String deviceRegId = registerFacebookUserProfile.getDeviceRegId();
		UserDevice userDevice = userDeviceHelper.getUserDeviceByRegId(deviceRegId);
		//find out if we have facebook account with given user name
		FacebookAccount facebookAccount = facebookAccountHelper.getFacebookAccountByUserName(registerFacebookUserProfile.getUserName());
		User user = null;
		if(facebookAccount == null){
			facebookAccount = new FacebookAccount();
			facebookAccount.setDateCreated(new Date());
			facebookAccount.setDateModified(new Date());
			facebookAccount.setToken(registerFacebookUserProfile.getAccessToken());
			facebookAccount.setUserName(registerFacebookUserProfile.getUserName());
			
			user = new User();
			user.setDateCreated(new Date());
			user.setDateModified(new Date());
			user.setEmail(registerFacebookUserProfile.getEmail());
			user.setMobile(registerFacebookUserProfile.getMobile());
			user.setName(registerFacebookUserProfile.getName());
			user.setVotingState(registerFacebookUserProfile.getVotingState());
			user.setLivingState(registerFacebookUserProfile.getLivingState());
			user.setUserDevices(new HashSet<UserDevice>());
			user.getUserDevices().add(userDevice);
			
		}else{
			facebookAccount.setDateModified(new Date());
			facebookAccount.setToken(registerFacebookUserProfile.getAccessToken());
			facebookAccount.setUserName(registerFacebookUserProfile.getUserName());

			user = facebookAccount.getUser();
			user.setDateModified(new Date());
			user.setEmail(registerFacebookUserProfile.getEmail());
			user.setMobile(registerFacebookUserProfile.getMobile());
			user.setName(registerFacebookUserProfile.getName());
			user.setVotingState(registerFacebookUserProfile.getVotingState());
			user.setLivingState(registerFacebookUserProfile.getLivingState());
			user.setUserDevices(new HashSet<UserDevice>());
			user.getUserDevices().add(userDevice);
		}
		
		user = userHelper.saveUser(user);
		facebookAccount.setUser(user);
		
		if(registerFacebookUserProfile.getLivingStateId() != null && registerFacebookUserProfile.getLivingStateId() > 0){
			State stateLiving = stateHelper.getStateById(registerFacebookUserProfile.getLivingStateId());		
			userDevice.setStateLiving(stateLiving);
			user.setStateLiving(stateLiving);
		}
		if(registerFacebookUserProfile.getLivingDistrictId() != null && registerFacebookUserProfile.getLivingDistrictId() > 0){
			District districtLiving = districtHelper.getDistrictById(registerFacebookUserProfile.getLivingDistrictId());		
			userDevice.setDistrictLiving(districtLiving);
			user.setDistrictLiving(districtLiving);
		}
		if(registerFacebookUserProfile.getLivingAcId() != null && registerFacebookUserProfile.getLivingAcId() > 0){
			AssemblyConstituency assemblyConstituencyLiving = assemblyConstituencyHelper.getAssemblyConstituencyById(registerFacebookUserProfile.getLivingAcId());		
			userDevice.setAssemblyConstituencyLiving(assemblyConstituencyLiving);
			user.setAssemblyConstituencyLiving(assemblyConstituencyLiving);
		}
		if(registerFacebookUserProfile.getVotingStateId() != null && registerFacebookUserProfile.getVotingStateId() > 0){
			State stateVoting = stateHelper.getStateById(registerFacebookUserProfile.getVotingStateId());		
			userDevice.setStateVoting(stateVoting);
			user.setStateVoting(stateVoting);
		}
		if(registerFacebookUserProfile.getVotingDistrictId() != null && registerFacebookUserProfile.getVotingDistrictId() > 0){
			District districtVoting = districtHelper.getDistrictById(registerFacebookUserProfile.getVotingDistrictId());		
			userDevice.setDistrictVoting(districtVoting);
			user.setDistrictVoting(districtVoting);
		}
		if(registerFacebookUserProfile.getVotingAcId() != null && registerFacebookUserProfile.getVotingAcId() > 0){
			AssemblyConstituency assemblyConstituencyVoting = assemblyConstituencyHelper.getAssemblyConstituencyById(registerFacebookUserProfile.getVotingAcId());		
			userDevice.setAssemblyConstituencyVoting(assemblyConstituencyVoting);
			user.setAssemblyConstituencyVoting(assemblyConstituencyVoting);
		}

		
		
		facebookAccount = facebookAccountHelper.saveFacebookAccount(facebookAccount);
		if(userDevice.getUsers() == null){
			userDevice.setUsers(new HashSet<User>());
			userDevice.getUsers().add(user);
		}
		
		
		userDevice = userDeviceHelper.saveUserDevice(userDevice);
		
		FacebookUserRegisteredMessage facebookUserRegisteredMessage = new FacebookUserRegisteredMessage();
		facebookUserRegisteredMessage.setDeviceRegId(registerFacebookUserProfile.getDeviceRegId());
		facebookUserRegisteredMessage.setUserId(user.getId());
		facebookUserRegisteredMessage.setStatus(TaskStatus.STATUS_COMPLETED);
		return facebookUserRegisteredMessage;
	}

	public RegisterFacebookUserResponse registerFacebookUserAsync(RegisterFacebookUserProfile registerFacebookUserProfile){
		RegisterFacebookUserResponse registerFacebookUserResponse = new RegisterFacebookUserResponse();
		try{
			RegisterFacebookUserTask registerFacebookUserTask = applicationContext.getBean(RegisterFacebookUserTask.class);
			registerFacebookUserTask.setRegisterFacebookUserProfile(registerFacebookUserProfile);
			threadPoolTaskExecutor.execute(registerFacebookUserTask);
			registerFacebookUserResponse.setStatus(TaskStatus.STATUS_RUNNING_IN_BACKGROUND);
		}catch(Exception ex){
			registerFacebookUserResponse.setStatus(TaskStatus.STATUS_FAILED);
		}
		return registerFacebookUserResponse;
	}

	public List<UserDeviceWeb> getAndroidUserDevices(long startId, int pageSize) throws AppException {
		List<UserDeviceWeb> returnList = new ArrayList<UserDeviceWeb>();
		List<UserDevice> dbDeviceList = userDeviceHelper.getAndroidUserDeviceAfterId(startId, pageSize);
		UserDeviceWeb oneUserDeviceWeb;
		for(UserDevice oneUserDevice:dbDeviceList){
			oneUserDeviceWeb = new UserDeviceWeb();
			oneUserDeviceWeb.setId(oneUserDevice.getId());
			oneUserDeviceWeb.setRegId(oneUserDevice.getRegId());
			returnList.add(oneUserDeviceWeb);
		}
		return returnList;
	}

	public List<StateDto> getAllStates() throws AppException {
		List<State> allStates = stateHelper.getAllStates();
		List<StateDto> returnList = new ArrayList<StateDto>();
		StateDto oneStateDto;
		for(State oneState:allStates){
			oneStateDto = new StateDto();
			oneStateDto.setId(oneState.getId());
			oneStateDto.setName(oneState.getName());
			oneStateDto.setDistrictDataAvailable(oneState.getDistrictDataAvailable());
			returnList.add(oneStateDto);
		}
		return returnList;
	}

	public List<DistrictDto> getAllDistrictOfState(long stateId)
			throws AppException {
		List<District> allDistricts = districtHelper.getDistrictOfState(stateId);
		List<DistrictDto> returnList = new ArrayList<DistrictDto>();
		DistrictDto oneDistrictDto;
		for(District oneDistrict:allDistricts){
			oneDistrictDto = new DistrictDto();
			oneDistrictDto.setId(oneDistrict.getId());
			oneDistrictDto.setName(oneDistrict.getName());
			oneDistrictDto.setAcDataAvailable(oneDistrict.getAcDataAvailable());
			returnList.add(oneDistrictDto);
		}
		return returnList;
	}

	public List<AssemblyConstituencyDto> getAllAssemblyConstituenciesOfDistrict(
			long districtId) throws AppException {
		List<AssemblyConstituency> allAssemblyConstituencies = assemblyConstituencyHelper.getAssemblyConstituencyOfDistrict(districtId);
		List<AssemblyConstituencyDto> returnList = new ArrayList<AssemblyConstituencyDto>();
		AssemblyConstituencyDto oneAssemblyConstituencyDto;
		for(AssemblyConstituency oneAssemblyConstituency:allAssemblyConstituencies){
			oneAssemblyConstituencyDto = new AssemblyConstituencyDto();
			oneAssemblyConstituencyDto.setId(oneAssemblyConstituency.getId());
			oneAssemblyConstituencyDto.setName(oneAssemblyConstituency.getName());
			returnList.add(oneAssemblyConstituencyDto);
		}
		return returnList;
	}

	public StateDto saveState(StateDto stateDto) throws AppException {
		State dbState;
		if(stateDto.getId() == null || stateDto.getId() <= 0){
			dbState = new State();
		}else{
			dbState = stateHelper.getStateById(stateDto.getId());
		}
		dbState.setName(stateDto.getName());
		dbState = stateHelper.saveState(dbState);
		
		stateDto.setId(dbState.getId());
		return stateDto;
	}

	public DistrictWeb saveDistrict(DistrictWeb districtWeb)
			throws AppException {
		District dbDistrict;
		if(districtWeb.getId() == null || districtWeb.getId() <= 0){
			dbDistrict = new District();
		}else{
			dbDistrict = districtHelper.getDistrictById(districtWeb.getId());
		}
		dbDistrict.setName(districtWeb.getName());
		
		State state = stateHelper.getStateById(districtWeb.getStateId());
		dbDistrict.setState(state);
		
		
		dbDistrict = districtHelper.saveDistrict(dbDistrict);
		
		districtWeb.setId(dbDistrict.getId());
		return districtWeb;

	}

	public AssemblyConstituencyWeb saveAssemblyConstituency(
			AssemblyConstituencyWeb assemblyConstituencyWeb)
			throws AppException {
		AssemblyConstituency dbAssemblyConstituency;
		if(assemblyConstituencyWeb.getId() == null || assemblyConstituencyWeb.getId() <= 0){
			dbAssemblyConstituency = new AssemblyConstituency();
		}else{
			dbAssemblyConstituency = assemblyConstituencyHelper.getAssemblyConstituencyById(assemblyConstituencyWeb.getId());
		}
		dbAssemblyConstituency.setName(assemblyConstituencyWeb.getName());
		District district = districtHelper.getDistrictById(assemblyConstituencyWeb.getDistrictId());
		dbAssemblyConstituency.setDistrict(district);
		
		dbAssemblyConstituency = assemblyConstituencyHelper.saveAssemblyConstituency(dbAssemblyConstituency);
		
		assemblyConstituencyWeb.setId(dbAssemblyConstituency.getId());
		return assemblyConstituencyWeb;
	}

	public List<DistrictWeb> saveDistricts(List<DistrictWeb> districtList)
			throws AppException {
		List<DistrictWeb> returnList = new ArrayList<DistrictWeb>();
		for(DistrictWeb oneDistrictWeb:districtList){
			returnList.add(saveDistrict(oneDistrictWeb));
		}
		return returnList;
	}

	public List<AssemblyConstituencyWeb> saveAssemblyConstituencies(
			List<AssemblyConstituencyWeb> assemblyConstituencyList)
			throws AppException {
		List<AssemblyConstituencyWeb> returnList = new ArrayList<AssemblyConstituencyWeb>();
		for(AssemblyConstituencyWeb oneAssemblyConstituencyWeb:assemblyConstituencyList){
			returnList.add(saveAssemblyConstituency(oneAssemblyConstituencyWeb));
		}
		return returnList;
	}

	public CandidateDto saveCandidate(CandidateDto candidateDto) throws AppException {
		Candidate dbCandidate = null;
		if(candidateDto.getId() != null && candidateDto.getId() > 0){
			dbCandidate = candidateHelper.getCandidateById(candidateDto.getId());
			if(dbCandidate == null){
				throw new AppException("No candidate found with id "+candidateDto.getId());
			}
		}else{
			dbCandidate = new Candidate();			
		}
		dbCandidate.setAddress(candidateDto.getAddress());
		dbCandidate.setContactNumber1(candidateDto.getContactNumber1());
		dbCandidate.setContactNumber2(candidateDto.getContactNumber2());
		dbCandidate.setEducation(candidateDto.getEducation());
		dbCandidate.setLegalCases(candidateDto.getLegalCases());
		dbCandidate.setName(candidateDto.getName());
		dbCandidate.setObjectives(candidateDto.getObjectives());
		dbCandidate.setProfile(candidateDto.getProfile());
		dbCandidate.setProfilePic(candidateDto.getProfilePic());
		dbCandidate.setSourceOfIncome(candidateDto.getSourceOfIncome());
		dbCandidate.setWealth(candidateDto.getWealth());
		dbCandidate.setAssemblyConstituencyId(candidateDto.getAssemblyConstituencyId());
		
		AssemblyConstituency dbAssemblyConstituency = assemblyConstituencyHelper.getAssemblyConstituencyById(candidateDto.getAssemblyConstituencyId());
		dbCandidate.setAssemblyConstituency(dbAssemblyConstituency);
		
		dbCandidate = candidateHelper.saveCandidate(dbCandidate);
		
		return convertCandidate(dbCandidate);
	}
	private CandidateDto convertCandidate(Candidate dbCandidate){
		CandidateDto candidateDto = new CandidateDto();
		
		candidateDto.setAddress(dbCandidate.getAddress());
		candidateDto.setContactNumber1(dbCandidate.getContactNumber1());
		candidateDto.setContactNumber2(dbCandidate.getContactNumber2());
		candidateDto.setEducation(dbCandidate.getEducation());
		candidateDto.setLegalCases(dbCandidate.getLegalCases());
		candidateDto.setName(dbCandidate.getName());
		candidateDto.setObjectives(dbCandidate.getObjectives());
		candidateDto.setProfile(dbCandidate.getProfile());
		candidateDto.setProfilePic(dbCandidate.getProfilePic());
		candidateDto.setSourceOfIncome(dbCandidate.getSourceOfIncome());
		candidateDto.setWealth(dbCandidate.getWealth());
		candidateDto.setId(dbCandidate.getId());
		candidateDto.setAssemblyConstituencyId(dbCandidate.getAssemblyConstituencyId());
		
		return candidateDto;
	}
	public List<CandidateDto> getAllCandidates() throws AppException {
		List<Candidate> candidates = candidateHelper.getAllCandidates();
		List<CandidateDto> allCandidate = new ArrayList<CandidateDto>();
		for(Candidate oneCandidate:candidates){
			allCandidate.add(convertCandidate(oneCandidate));
		}
		return allCandidate;
	}

	public ManifestoDto saveManifesto(ManifestoDto manifestoDto)
			throws AppException {
		Manifesto dbManifesto = null;
		if(manifestoDto.getId() != null && manifestoDto.getId() > 0){
			dbManifesto = manifestoHelper.getManifestoById(manifestoDto.getId());
			if(dbManifesto == null){
				throw new AppException("No Manifesto found with id "+manifestoDto.getId());
			}
		}else{
			dbManifesto = manifestoHelper.getManifestoOfAssemblyConstituency(manifestoDto.getAssemblyConstituencyId());
			if(dbManifesto == null){
				dbManifesto = new Manifesto();	
			}
		}
		dbManifesto.setContent(manifestoDto.getContent());
		AssemblyConstituency dbAssemblyConstituency = assemblyConstituencyHelper.getAssemblyConstituencyById(manifestoDto.getAssemblyConstituencyId());
		dbManifesto.setAssemblyConstituency(dbAssemblyConstituency);
		
		dbManifesto = manifestoHelper.saveManifesto(dbManifesto);
		manifestoDto.setId(dbManifesto.getId());
		return manifestoDto;
	}

	public List<ManifestoDto> getAllManifestos() throws AppException {
		List<Manifesto> allManifestos = manifestoHelper.getAllManifestos();
		List<ManifestoDto> returnManifestos = new ArrayList<ManifestoDto>();
		ManifestoDto oneManifestoDto;
		for(Manifesto oneManifesto:allManifestos){
			oneManifestoDto = new ManifestoDto();
			oneManifestoDto.setId(oneManifesto.getId());
			oneManifestoDto.setContent(oneManifesto.getContent());
			oneManifestoDto.setAssemblyConstituencyId(oneManifesto.getAssemblyConstituencyId());
			returnManifestos.add(oneManifestoDto);
		}
		return returnManifestos;
	}

	


}
